package cn.xeblog.plugin.tools.read.entity;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 章节
 *
 * @author LYF
 * @date 2022-07-18
 */
@NoArgsConstructor
@Data
public class Chapter {

    /**
     * 书籍路径
     */
    private String bookUrl;
    /**
     * 下标
     */
    private Integer index;
    /**
     * 标题
     */
    private String title;
    /**
     * 章节起始位置
     */
    private Long startPointer;
    /**
     * 章节结束位置
     */
    private Long endPointer;

    /**
     * 构造
     *
     * @param title        标题
     * @param startPointer 开始点
     * @param endPointer   结束点
     */
    public Chapter(String title, Long startPointer, Long endPointer) {
        this.title = title;
        this.startPointer = startPointer;
        this.endPointer = endPointer;
        this.index = 0;
    }

    @Override
    public String toString() {
        return title.trim();
    }

    /**
     * 生成章节
     *
     * @param file    文件
     * @param charset 章节
     * @param pattern 模式
     * @return 章节列表
     * @throws Exception
     */
    public static List<Chapter> generateChapter(String file, Charset charset, String pattern) throws Exception {
        List<Chapter> chapterList = new ArrayList<>();

        // 文件流
        try (FileInputStream in = new FileInputStream(file)) {
            // 读取的字节缓存
            byte[] readBuff = new byte[1024];
            // 章节起始位置
            long pointer = 0L;
            // 是否终止
            boolean eol = false;
            // 结束的长度
            int endLength = 0;

            // 字节输出流
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // 读取的长度
            int readCount;
            // 只要能读取到字节个数，就一直进行
            while ((readCount = in.read(readBuff)) != -1) {
                for (int i = 0; i < readCount; ++i) {
                    byte tmp;
                    switch (tmp = readBuff[i]) {
                        case 10:
                            // 换行
                            eol = true;
                            ++endLength;
                            break;
                        case 13:
                            // 回车
                            ++endLength;
                            // 超出可读字节容量，或者下一字节不是换行
                            eol = i + 1 >= readCount || readBuff[i + 1] != 10;
                            break;
                        default:
                            //
                            bos.write(tmp);
                    }

                    // 终止时
                    if (eol) {
                        // 处理行
                        pointer += handleLine(chapterList, bos, charset, pointer, endLength, pattern);
                        endLength = 0;
                        eol = false;
                    }
                }
            }
        } catch (Throwable t) {
            throw t;
        }
        return chapterList;
    }

    /**
     * 处理行
     *
     * @param chapterList  章节列表
     * @param bos          字节数组输出流
     * @param charset      字符集
     * @param startPointer 章节起始位置
     * @param endLength    章节结束位置
     * @param pattern      模式
     * @return 行结束时最后所在的位置，即下一行开始的位置
     */
    private static int handleLine(List<Chapter> chapterList, ByteArrayOutputStream bos, Charset charset, long startPointer, int endLength, String pattern) {
        byte[] data = bos.toByteArray();
        // 设置字符集
        String line = new String(data, charset);

        // 章节匹配
        if (Pattern.matches(pattern, line)) {
            // 当前章节
            Chapter currentChapter = new Chapter(line, startPointer, 0L);
            // 章节列表的数量，即章节号
            int index = chapterList.size();

            // 设置章节结束位置
            if (index > 0) {
                Chapter lastChapter = chapterList.get(index - 1);
                lastChapter.setEndPointer(startPointer);
            }
            chapterList.add(currentChapter);
        }
        // 重置字节数组输出流，即清空内容
        bos.reset();

        // 数据的长度
        return data.length + endLength;
    }
}
